OpenBlock.BlocklyParser.JSONConvert = (function () {
    class BlocklyFSM {

        // currentState;
        stack;
        errors = [];
        toAst = new OpenBlock.BlocklyParser.BlockToAST();
        src;
        constructor(src, stack) {
            // super();
            // this.currentState = new BlocklyFSMState(this);
            this.src = src;
            this.stack = stack;
            this.toAst._stack = stack;
        }
        makeAST(v) {
            let toAST;
            if (v.disabled || v.enabled === false) {
                toAST = this.toAst['dummy'];
            } else {
                toAST = this.toAst[v.type];
            }
            if (!toAST) {
                debugger;
                throw Error('Unknown AST of block type ' + v.type);
            }
            return toAST;
        }
        reviver(k, v, obj) {
            if (k == 'extraState' && typeof (v) == 'string' && v.startsWith('<mutation')) {
                let mutation = OpenBlock.BlocklyParser.JSONConvert.xmlToMutation(v);
                obj.mutation = mutation;
            } else if (k == 'block' || k == 'shadow') {
                v.AST = this.makeAST(v).bind(this.toAst);
            } else if (k == 'blocks' && Array.isArray(v)) {
                v.forEach(b => {
                    b.AST = this.makeAST(b).bind(this.toAst);
                });
            }

            return v;
        }
        buildReviver() {
            let that = this;
            return function (k, v) {
                return that.reviver(k, v, this);
            };
        }
    }
    class Convert {
        src; stack;
        constructor(src, stack) {
            this.src = src;
            this.stack = stack;
        }
        blocklyToAST(json) {
            let fsm = new BlocklyFSM(this.src, this.stack);
            let codeObj = JSON.parse(json, fsm.buildReviver());
            if (codeObj?.blocks?.blocks) {
                return codeObj.blocks.blocks;
            } else {
                return [];
            }
        }
        static xmlToMutation(xml) {
            let mutation = {};
            let parser = new EasySAXParser({
                on: {
                    startNode(elem, getAttrs, isTagEnd, getStringNode) {
                        /*
                         * start->XML,
                         * Block,
                         * Mutation(xml),
                         * Value(Block),
                         * Field(text),
                         * Next(Block),
                         * Comment(text),
                         * Statement(block)
                         */
                        switch (elem) {
                            case 'mutation':
                                Object.assign(mutation, { elementName: elem }, getAttrs());
                                break;
                            default:
                                console.error('unknown element type:' + {
                                    elem, attrs: getAttrs(), isTagEnd,
                                    StringNode: getStringNode()
                                });
                        }
                    },
                    textNode(t) {
                        mutation.text = t;
                    },
                    // endNode(elem, isTagStart, getStringNode) {
                    //     if (elem !== 'mutation') {
                    //         throw Error('???');
                    //     }
                    //     mutation = this;
                    // }
                    // , OnNodeFinished() {
                    //     mutation = this;
                    // }
                }
            });
            parser.parse(xml);
            return mutation;
        }
        static convert(json, codename, src, parsed, stack) {
            let typeMap = {
                EventHandlerDef: parsed.eventHandlers,
                MessageHandlerDef: parsed.messageHandlers,
                FunctionDef: parsed.functions,
                StructDef: parsed.structs
            }
            let blocks = new Convert(src, stack).blocklyToAST(json);
            if (blocks) {
                blocks.forEach(b => {
                    if (OpenBlock.Utils.topBlockType.indexOf(b.type) == -1) {
                        return;
                    }
                    // if (b.extraState && typeof (b.extraState) == 'string' && b.extraState.startsWith('<mutation ')) {
                    //     b.mutation = Convert.xmlToMutation(b.extraState);
                    // }
                    try {
                        let ast = b.AST(b);
                        if (ast && typeMap[ast.__proto__.constructor.name]) {
                            typeMap[ast.__proto__.constructor.name].push(ast);
                        } else {
                            // 忽略随意摆放的块
                            // throw Error('非预期的 ' + ast.__proto__.constructor.name);
                        }
                    } catch (e) {
                        console.warn(e);
                    }
                });
            }
            for (let k in parsed) {
                let arr = parsed[k];
                arr.forEach((v) => {
                    v.codename = codename;
                });
            }
            return parsed;
        }
    };
    return Convert;
})()